#include "bllcontroller.h"
#include <QDebug>
#include <QMetaType>
#include "log4z.h"
#include "userlogger.h"

BllController * BllController::instance = nullptr;

BllController::BllController(QObject *parent) : QObject(parent), curStatus(Status::Startup)
{
    qDebug() <<"BllController()" << "thread:" <<QThread::currentThreadId();
    isOpenPort = false;
    ctrlSerial = new Controller();
    ctrlThread = new QThread();
    ctrlSerial->moveToThread(ctrlThread);

    qRegisterMetaType<LedType>("LedType");
    qRegisterMetaType<CMD_TYPE>("COMM_TYPE");
    qRegisterMetaType<ComCmd>("ComCmd");
    qRegisterMetaType<SHK_Result>("SHK_Result");
    qRegisterMetaType<FinishMessage>("FinishMessage");
    qRegisterMetaType<HostReply>("HostReply");

    //Signals and Slots
    connect(ctrlThread, &QThread::finished, ctrlSerial,&QObject::deleteLater);
    connect(this, &BllController::openSerialPortSignal, ctrlSerial, &Controller::openSerialPortSlot);
    connect(this, &BllController::sendPortCmdSignal, ctrlSerial, &Controller::sendPortCmdSlot);
    connect(this, &BllController::lightLedSignal, ctrlSerial, &Controller::lightLedSlot);
    connect(ctrlSerial, &Controller::dataReceivedSignal, this, &BllController::dataReceivedSlot);
    connect(this, &BllController::startPostSignal, ctrlSerial, &Controller::POST);
    connect(ctrlSerial, &Controller::progressChangeSignal, this,&BllController::progressChangeSignal);
    connect(ctrlSerial, &Controller::expResultPassSignal, this, &BllController::expResultPassSignal);
    connect(ctrlSerial, &Controller::expResultFailSignal, this, &BllController::expResultFailSignal);

    //
    connect(ctrlSerial, &Controller::selfCheckResultFailSignal, this, &BllController::selfCheckResultFailSignal);
    connect(ctrlSerial, &Controller::selfCheckResultPassSignal, this, &BllController::selfCheckResultPassSignal);

    //pwd and flw check
    connect(ctrlSerial, &Controller::pwdflwCheckResultSignal, this, &BllController::pwdflwCheckResultSignal);

    //SHK
    connect(ctrlSerial, &Controller::shkCheckResultSignal, this, &BllController::shkCheckResultSignal);

    //Start Experiment
    connect(ctrlSerial, &Controller::experimentStartSignal, this, &BllController::experimentStartSignal);

    connect(ctrlSerial, &Controller::hostVersionSignal, this, &BllController::hostVersionSignal);

    connect(ctrlSerial, &Controller::liquidDetectedSignal, this, &BllController::liquidDetectedSignal);

    connect(ctrlSerial, &Controller::motorCalibDoneSignal, this, &BllController::motorCalibDoneSignal);

    ctrlThread->start();
    qDebug()<<"ctrlThread->isRunning():" << ctrlThread->isRunning();

}

void BllController::openSerialPort(const QString & comName)
{
    if(!ctrlThread->isRunning())
    {
        ctrlThread->start();
    }
    emit openSerialPortSignal(comName);
}

void BllController::sendPortCmd(ComCmd cmd)
{
    emit sendPortCmdSignal(cmd);
}

void BllController::expResultPass(const HostReply &data)
{
    emit expResultPassSignal(data);
}

void BllController::expResultFail(const HostReply &data)
{
    emit expResultFailSignal(data);
}

void BllController::setInstrumentStauts(const Status &status)
{
    if (curStatus == status) return;

    const QMap<Status, QPair<LedType, QString>> map = {
        { Status::Startup,    {LedType::Blue,  "Startup" }},
        { Status::Idle,       {LedType::Green, "Idle" }},
        { Status::Experiment, {LedType::Green, "Experiment" }},
        { Status::Busy,       {LedType::Blue,  "Busy"}},
        { Status::Error,      {LedType::Red,   "Error"}},
        { Status::MotorCalib, {LedType::Blue,  "MotorCalib"}},
    };

    Q_ASSERT(map.contains(status));
    LOGFMTD("Instrument Stauts: %s -> %s", map[curStatus].second.toStdString().c_str(), map[status].second.toStdString().c_str());
    curStatus = status;
}

Status BllController::instrumentStatus()
{
    return curStatus;
}

void BllController::queryHostVersion()
{
    ComCmd cmd;
    cmd.type = CMD_TYPE::VERNUM;
    sendPortCmd(cmd);
    LOGFMTD("query host version");
}

void BllController::startPost()
{
    emit startPostSignal();
}

void BllController::dataReceivedSlot(QString data)
{
    LOGFMTD("%s(): %s", __FUNCTION__, data.toStdString().c_str());
    UserLogger::debug("->" + data);
}


BllController *BllController::getInstance()
{
    static QMutex mutex;
    if (!instance)
    {
        qDebug() <<"BllController getInstance()" << "thread:" <<QThread::currentThreadId();
        QMutexLocker locker(&mutex);
        if(!instance)
        {
            instance  = new BllController();
        }
    }
    return instance;
}

BllController::~BllController()
{
    qDebug() <<"~BllController()" << "thread:" <<QThread::currentThreadId();
    if(ctrlSerial)
    {
        ctrlSerial->close();
        ctrlSerial->deleteLater();
    }

    if (ctrlThread->isRunning())
    {
        ctrlThread->quit();
        ctrlThread->wait();
    }
}
